#include "Subsystems/BlockBehaviors/SubsystemTreasureGeneratorBlockBehavior.h"
#include "GameSingleton.h"
#include "GameRandom.hpp"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemPickables.h"

#include "Blocks/AirBlock.h"
#include "Blocks/TreasureGeneratorBlock.h"
#include "Blocks/ArrowBlock.h"
#include "Blocks/WaterBlock.h"
#include "Blocks/ExperienceBlock.h"

namespace PocketSurvival
{
    struct TreasureData
    {
        constexpr inline TreasureData(int32_t theValue, int32_t theMaxCount, float theProbability) : value(theValue), maxCount(theMaxCount), probability(theProbability) {}
        int32_t value;
        int32_t maxCount;
        float probability;
    };

    const TreasureData treasureDataArray[] = {
        TreasureData(79, 4, 4.0f),
        TreasureData(111, 1, 1.0f),
        TreasureData(43, 4, 4.0f),
        TreasureData(40, 3, 2.0f),
        TreasureData(42, 3, 4.0f),
        TreasureData(22, 4, 4.0f),
        TreasureData(103, 4, 2.0f),
        TreasureData(150, 1, 1.0f),
        TreasureData(21, 16, 2.0f),
        TreasureData(159, 4, 2.0f),
        TreasureData(207, 4, 2.0f),
        TreasureData(17, 2, 2.0f),
        TreasureData(31, 4, 4.0f),
        TreasureData(108, 8, 4.0f),
        TreasureData(109, 4, 2.0f),
        TreasureData(105, 4, 1.0f),
        TreasureData(106, 2, 1.0f),
        TreasureData(107, 1, 1.0f),
        TreasureData(234, 4, 1.0f),
        TreasureData(235, 2, 1.0f),
        TreasureData(236, 1, 1.0f),
        TreasureData(132, 2, 2.0f),
        TreasureData(Terrain::MakeBlockValue(173, 0, 6), 8, 2.0f),
        TreasureData(Terrain::MakeBlockValue(173, 0, 7), 8, 8.0f),
        TreasureData(Terrain::MakeBlockValue(173, 0, 5), 8, 8.0f),
        TreasureData(Terrain::MakeBlockValue(173, 0, 6), 8, 2.0f),
        TreasureData(Terrain::MakeBlockValue(119, 0, 0), 8, 2.0f),
        TreasureData(Terrain::MakeBlockValue(119, 0, 1), 8, 2.0f),
        TreasureData(Terrain::MakeBlockValue(119, 0, 2), 8, 2.0f),
        TreasureData(Terrain::MakeBlockValue(119, 0, 3), 8, 2.0f),
        TreasureData(Terrain::MakeBlockValue(119, 0, 4), 8, 2.0f),
        TreasureData(191, 1, 4.0f),
        TreasureData(Terrain::MakeBlockValue(192, 0, ArrowBlock::SetArrowType(0, ArrowType::CopperArrow)), 2, 2.0f),
        TreasureData(Terrain::MakeBlockValue(192, 0, ArrowBlock::SetArrowType(0, ArrowType::IronArrow)), 2, 2.0f),
        TreasureData(Terrain::MakeBlockValue(192, 0, ArrowBlock::SetArrowType(0, ArrowType::DiamondArrow)), 2, 1.0f),
        TreasureData(Terrain::MakeBlockValue(192, 0, ArrowBlock::SetArrowType(0, ArrowType::FireArrow)), 2, 2.0f),
        TreasureData(200, 1, 1.0f),
        TreasureData(Terrain::MakeBlockValue(192, 0, ArrowBlock::SetArrowType(0, ArrowType::IronBolt)), 2, 2.0f),
        TreasureData(Terrain::MakeBlockValue(192, 0, ArrowBlock::SetArrowType(0, ArrowType::DiamondBolt)), 2, 1.0f),
        TreasureData(Terrain::MakeBlockValue(192, 0, ArrowBlock::SetArrowType(0, ArrowType::ExplosiveBolt)), 2, 1.0f),
        TreasureData(212, 1, 1.0f),
        TreasureData(124, 1, 1.0f),
        TreasureData(125, 1, 1.0f),
        TreasureData(82, 1, 1.0f),
        TreasureData(116, 1, 1.0f),
        TreasureData(36, 1, 1.0f),
        TreasureData(113, 1, 1.0f),
        TreasureData(38, 1, 1.0f),
        TreasureData(115, 1, 1.0f),
        TreasureData(37, 1, 1.0f),
        TreasureData(114, 1, 1.0f),
        TreasureData(171, 1, 1.0f),
        TreasureData(172, 1, 1.0f),
        TreasureData(90, 1, 1.0f),
        TreasureData(160, 1, 1.0f),
        TreasureData(158, 1, 2.0f),
        TreasureData(133, 10, 1.0f),
        TreasureData(179, 2, 1.0f),
        TreasureData(142, 2, 1.0f),
        TreasureData(141, 2, 1.0f),
        TreasureData(237, 2, 1.0f)};

    // 编译时计算
    constexpr float getAllProbability()
    {
        float probability = 0;
        for (int32_t index = 0; index < (sizeof(treasureDataArray) / sizeof(treasureDataArray[0])); index++)
        {
            probability += treasureDataArray[index].probability;
        }
        return probability;
    }

    SubsystemTreasureGeneratorBlockBehavior::SubsystemTreasureGeneratorBlockBehavior()
    {
        m_handleBlocks.push_back(TreasureGeneratorBlock::Index());
    }

    void SubsystemTreasureGeneratorBlockBehavior::onNeighborBlockChanged(int32_t x, int32_t y, int32_t z, int32_t neighborX, int32_t neighborY, int32_t neighborZ)
    {
        int32_t cellContents = m_terrain->getCellContents(neighborX, neighborY, neighborZ);
        if (cellContents != AirBlock::Index() && cellContents != WaterBlock::Index())
        {
            return;
        }
        m_terrain->changeCell(x, y, z, Terrain::MakeBlockValue(AirBlock::Index()));

        // 25% 的可能性有东西掉落
        if (false == GameSingleton::gameRandom->getBool(0.25f))
        {
            return;
        }
        int32_t blockValue = 0;
        int32_t blockCount = 0;

        // 计算总的可能性，然后随机区间，得到掉落的物品进行掉落
        float max = getAllProbability();
        float num3 = GameSingleton::gameRandom->getFloat(0.0f, max);
        for (int32_t index = 0; index < (sizeof(treasureDataArray) / sizeof(treasureDataArray[0])); index++)
        {
            num3 -= treasureDataArray[index].probability;
            if (num3 <= 0.0f)
            {
                blockValue = treasureDataArray[index].value;
                blockCount = GameSingleton::gameRandom->getInt(1, treasureDataArray[index].maxCount);
                break;
            }
        }
        
        if (blockValue != 0 && blockCount > 0)
        {
            for (int32_t j = 0; j < blockCount; j++)
            {
                m_pickables->addPickable(blockValue, 1, Vector3(x, y, z) + GameSingleton::gameRandom->getVector3(0.1f, 0.4f) + 0.5f, std::make_shared<Vector3>(), nullptr);
            }
            int32_t num4 = GameSingleton::gameRandom->getInt(3, 6);
            for (int32_t k = 0; k < num4; k++)
            {
                m_pickables->addPickable(ExperienceBlock::Index(), 1, Vector3(x, y, z) + GameSingleton::gameRandom->getVector3(0.1f, 0.4f) + 0.5f, std::make_shared<Vector3>(), nullptr);
            }
        }
    }

    void SubsystemTreasureGeneratorBlockBehavior::load(const nlohmann::json &json)
    {
        m_pickables = GameSingleton::singletonPtr->getSingleton<SubsystemPickables>();
        m_terrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();
    }

    static const std::string subsystemName = "TreasureGeneratorBlockBehavior";
    const std::string &SubsystemTreasureGeneratorBlockBehavior::getName() const
    {
        return subsystemName;
    }
}
